////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef __WN_SCRIPTING_FUNCTION_MANAGER_H__
#define __WN_SCRIPTING_FUNCTION_MANAGER_H__

#include "WNScripting/inc/WNIdentifierMap.h"
#include "WNScripting/inc/WNTypeManager.h"
#include "WNScripting/inc/WNScriptingEnginePublic.h"
#include "WNScripting/inc/WNFunction.h"

#include <iostream>

namespace WNScripting {
    #define DEFINE_FUNCTION(mgr, returntype, _func, ...)  mgr->AddExternalFunction(ptr, returntype, #_func, ##__VA_ARGS__)

    struct WNScriptingEngineData;
    class WNScriptingEngine;

    static WN_VOID* StaticGenerationFunction(WNScriptingEngine* _engine, WNFunction* _function) {

        if (_engine && _function) {
            _engine->CompileFunction(_function);
        }

        return(_function->mFunctionPointer);
    }

    class WNFunctionManager {
    public:
        enum ReturnCode {
            #include "WNCore/inc/WNErrors.inc"
            WN_FUNCTION_MANAGER_DOES_NOT_EXIST,
            WN_FUNCTION_MANAGER_INVALID_IDENTIFIER,
            WN_FUNCTION_MANAGER_INVALID_CONVENTION,
            WN_FUNCTION_MANAGER_CONFLICTING_DEFINITION,
            WN_FUNCTION_MANAGER_IDENTICAL_DUPLICATES
        };

        WNFunctionManager(WNScriptingEngineData& _scriptingData);
        WN_VOID AddNewThread();

        WN_VOID CreateInternalFunction(WNFunction*& _funcLocation, const WN_CHAR* _functionName, WN_VOID* _funcPtr, llvm::Type* _ptrType, std::vector<llvm::Type*> _llvmTypes);

        ~WNFunctionManager();
        ReturnCode AddExternalFunction(WN_VOID* _funcPtr, WNIdentifierElement _name, WNTypeElement _returnType, std::vector<WNTypeElement>& _params);
        ReturnCode AddExternalFunction(WN_VOID* _funcPtr, WN_BOOL _stack, const WN_CHAR* _returnType, const WN_CHAR* _name, const WN_CHAR* _arglist);
        ReturnCode AddIncompleteScriptFunction(WNIdentifierElement _name, WNIdentifierElement _file, WN_BOOL _onStack, WNTypeElement _returnType, const std::vector<WNTypeElement>& _params, WN_SIZE_T _offset, WN_SIZE_T _endOffset);
        WN_VOID GetScriptFunctions(std::vector<WNFunction*>& _functions, WNIdentifierElement _name, WNIdentifierElement _file);
        WN_VOID GetExternalFunctions(WNIdentifierElement _name, std::vector<WNFunction*>& _functions, WN_BOOL _eTypes);
        ReturnCode GetExactScriptFunction(WNFunction*& _function, WNIdentifierElement _name, const std::vector<WNTypeElement>& _params, WNIdentifierElement _file);
    
        WNIdentifierMap* GetIdentifierMap();
        WNFunction* GetCreateFunctionFunction();
        WNFunction* GetMallocFunction();
        WNFunction* GetFreeFunction();
        WNFunction* GetMemcpyFunction();
        WNFunction* GetTemporaryStackFunction();
        WN_VOID SetMallocFunction(WNScriptAllocFunc _scriptFunc);
        WN_VOID SetFreeFunction(WNScriptFreeFunc _freeFunc);
        WN_VOID SetAlignedMallocFunction(WNScriptAlignedMallocFunc _alignedFunc);
        WN_VOID SetAlignedFreeFunction(WNScriptAlignedFree _alignedFunc);
        WN_VOID SetMemcpyFunction(WNScriptMemcpy _memcpy);

        WN_VOID* CheckSystemFunctions(const std::string& _function);
        WN_VOID AddIncludeFile(WNIdentifierElement _file, WNIdentifierElement _includedFile);
    private:
        typedef WN_UNORDERED_MULTIMAP<WNIdentifierElement, WNFunction*> FunctionMMap;
        typedef WN_UNORDERED_MULTIMAP<WNIdentifierElement, WNIdentifierElement> IncludeMMap;

        WN_BOOL ContainsFile(IncludeMMap::iterator& _start, IncludeMMap::iterator& _end, WNIdentifierElement _element);
        WN_VOID GenerateLLVMFunction(WNFunction*, WN_BOOL);
        WNScriptingEngineData& mScriptingData;
        FunctionMMap mFunctionMap;
        FunctionMMap mNativeMap;
        IncludeMMap mIncludeMap;
        WNFunction* mCreateFunctionfunction;
        WNFunction* mMallocFunction;
        WNFunction* mFreeFunction;
        WNFunction* mAlignedFreeFunction;
        WNFunction* mAlignedMallocFunction;
        WNFunction* mMemcpyFunction;
        WNFunction* mInternalTempStackFunction;

        WNScriptAllocFunc mInternalMallocFunc;
        WNScriptFreeFunc  mInternalFreeFunc;
        WNScriptAlignedMallocFunc mInternalAlignedMalloc;
        WNScriptAlignedFree mInternalAlignedFree;
        WNScriptMemcpy   mInternalMemcpy;
    };
}
#endif // __WN_SCRIPTING_FUNCTION_MANAGER_H__
